package com.hzw.saas.service.schedule.service;

import cn.hutool.core.util.StrUtil;
import com.baomidou.mybatisplus.core.metadata.IPage;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.baomidou.mybatisplus.extension.service.IService;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.google.common.collect.Sets;
import com.hzw.saas.api.schedule.IUserScheduleService;
import com.hzw.saas.api.schedule.bo.UserScheduleBo;
import com.hzw.saas.api.schedule.bo.UserScheduleViewBo;
import com.hzw.saas.api.schedule.query.*;
import com.hzw.saas.common.config.util.PageParam;
import com.hzw.saas.common.util.MapperUtil;
import com.hzw.saas.common.util.SnowFlakeUtil;
import com.hzw.saas.service.schedule.constants.UserScheduleConst;
import com.hzw.saas.service.schedule.mapper.UserScheduleMapper;
import com.hzw.saas.service.schedule.model.UserSchedule;
import com.hzw.saas.service.schedule.pojo.MeetingCheckInfo;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.util.CollectionUtils;

import javax.annotation.Resource;
import java.time.LocalDate;
import java.time.temporal.ChronoUnit;
import java.util.*;
import java.util.stream.Collectors;
import java.util.stream.Stream;

/**
 * @author hankun
 */
@Service
public class UserScheduleServiceImpl extends ServiceImpl<UserScheduleMapper, UserSchedule> implements IService<UserSchedule>, IUserScheduleService {

    @Resource
    private UserScheduleTagRelServiceImpl userScheduleTagRelService;

    @Override
    @Transactional(rollbackFor = Exception.class)
    public Boolean addSchedule(UserScheduleAddQuery userScheduleAddQuery) {
        String userId = userScheduleAddQuery.getUserId();
        UserSchedule userSchedule = MapperUtil.nf().map(userScheduleAddQuery, UserSchedule.class);
        userSchedule.setSortOrder(getNextSortOrder(userId));
        userSchedule.setPid(SnowFlakeUtil.nextIdStr());
        userSchedule.setStatus(UserSchedule.STATUS_NOT_CHECK);
        userScheduleTagRelService.addUserScheduleTagRel(userSchedule.getPid(), userScheduleAddQuery.getTagIds());
        return super.save(userSchedule);
    }

    @Override
    public Boolean addBatchSchedule(UserScheduleAddQuery userScheduleAddQuery, Set<String> userIds) {
        List<UserSchedule> userSchedules = userIds.stream()
                .map(u -> {
                    UserSchedule userSchedule = MapperUtil.nf().map(userScheduleAddQuery, UserSchedule.class);
                    userSchedule.setUserId(u);
                    userSchedule.setSortOrder(getNextSortOrder(u));
                    userSchedule.setCreatorId(userScheduleAddQuery.getUserId());
                    userSchedule.setPid(SnowFlakeUtil.nextIdStr());
                    userSchedule.setStatus(UserSchedule.STATUS_NOT_CHECK);
                    return userSchedule;
                })
                .collect(Collectors.toList());
        return super.saveBatch(userSchedules);
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public Boolean updateSchedule(UserScheduleUpdateQuery userScheduleUpdateQuery) {
        return super.lambdaQuery().eq(UserSchedule::getPid, userScheduleUpdateQuery.getPid())
                .oneOpt()
                // 排除会议类
                .filter(u -> StrUtil.isBlank(u.getMeetingId()))
                .map(u -> {
                    UserSchedule userSchedule = MapperUtil.nf().map(userScheduleUpdateQuery, UserSchedule.class);
                    userScheduleTagRelService.updateUserScheduleTagRel(u.getPid(), userScheduleUpdateQuery.getTagIds());
                    return super.updateById(userSchedule);
                })
                .orElse(Boolean.FALSE);
    }

    @Override
    public IPage<UserScheduleBo> getSchedule(UserScheduleQuery userScheduleQuery, Page<UserScheduleBo> page) {
        userScheduleQuery.initAshcanFlag();
        return this.getBaseMapper().getSchedule(userScheduleQuery, page);
    }

    @Override
    public List<UserScheduleViewBo> getScheduleView(UserScheduleViewQuery userScheduleViewQuery) {
        UserScheduleQuery query = new UserScheduleQuery()
                .setUserId(userScheduleViewQuery.getUserId())
                .setAshcanFlag(0)
                .setStatus(Collections.singletonList(0))
                .setStartTime(userScheduleViewQuery.getStartDateTime())
                .setEndTime(userScheduleViewQuery.getEndDateTime());
        Map<LocalDate, List<UserScheduleBo>> viewMap = getSchedule(query, PageParam.getBigPageParam().convert())
                .getRecords()
                .stream()
                .collect(Collectors.groupingBy(u -> u.getDueDate().toLocalDate()));

        LocalDate tempStartDate = userScheduleViewQuery.getStartDateTime().toLocalDate();
        LocalDate tempEndDate = userScheduleViewQuery.getEndDateTime().toLocalDate().minusDays(1);
        long numOfDay = ChronoUnit.DAYS.between(tempStartDate, tempEndDate);
        return Stream.iterate(tempStartDate, start -> start.plusDays(1))
                .limit(numOfDay)
                .map(d -> {
                    List<UserScheduleBo> scheduleBos = viewMap.get(d);
                    boolean isNull = Objects.isNull(scheduleBos);
                    return new UserScheduleViewBo()
                            .setDate(d)
                            .setScheduleNum(isNull ? 0 : scheduleBos.size())
                            .setUserScheduleBos(isNull ? Collections.emptyList() : scheduleBos);
                })
                .collect(Collectors.toList());
    }

    @Override
    public Boolean check(UserScheduleCheckQuery checkQuery) {
        return super.lambdaQuery()
                .eq(UserSchedule::getPid, checkQuery.getPid())
                .oneOpt()
                .map(userSchedule -> {
                    if (Objects.isNull(checkQuery.getNextDueDate())) {
                        // 无下次提醒 -> 当前提醒规则清除并标记已完成
                        userSchedule.setRepeatFlag(StrUtil.EMPTY);
                        userSchedule.setStatus(UserSchedule.STATUS_CHECK);
                        userSchedule.setEditorId(userSchedule.getUserId());
                    } else {
                        // 有下次提醒 -> 当前提醒日期更改为下次提醒日期并新增一条提醒规则为空的已完成相同数据
                        UserSchedule checkUserSchedule = MapperUtil.nf().map(userSchedule, UserSchedule.class);
                        checkUserSchedule.setRepeatFlag(StrUtil.EMPTY);
                        checkUserSchedule.setStatus(UserSchedule.STATUS_CHECK);
                        checkUserSchedule.setPid(SnowFlakeUtil.nextIdStr());
                        checkUserSchedule.setCheckPid(checkQuery.getPid());
                        super.save(checkUserSchedule);

                        userSchedule.setEditorId(userSchedule.getUserId());
                        userSchedule.setDueDate(checkQuery.getNextDueDate());
                        userSchedule.setUserId(checkQuery.getRepeatFlag());
                    }
                    return super.updateById(userSchedule);
                })
                .orElse(Boolean.FALSE);
    }

    @Override
    public Boolean deleteByIds(List<String> pidList, String userId) {
        return super.lambdaUpdate()
                .eq(UserSchedule::getUserId, userId)
                .eq(UserSchedule::getIsDelete, 0)
                .eq(UserSchedule::getAshcanFlag, 1)
                // 为空 清除全部
                .in(!CollectionUtils.isEmpty(pidList), UserSchedule::getPid, pidList)
                .remove();
    }

    /**
     * 统计历史check数量 包括已删除
     *
     * @param pid pid
     * @return Integer
     */
    private Integer historyCheckCount(String pid) {
        return super.getBaseMapper().historyCheckCount(pid);
    }

    private Long getNextSortOrder(String userId) {
        UserSchedule oneSchedule = super.lambdaQuery()
                .eq(UserSchedule::getUserId, userId)
                .orderByDesc(UserSchedule::getSortOrder)
                .last("limit 1")
                .one();
        return Objects.isNull(oneSchedule) ? 0 : oneSchedule.getSortOrder() + UserScheduleConst.INTERVAL_SORT;
    }

    public Boolean updateMeeting(UserScheduleMeetingUpdateQuery updateQuery) {
        return super.lambdaQuery()
                .eq(UserSchedule::getUserId, updateQuery.getUserId())
                .eq(UserSchedule::getMeetingId, updateQuery.getMeetingId())
                .last(" limit 1")
                .oneOpt()
                .map(s -> {
                    s.setAheadTime(updateQuery.getAheadTime());
                    return super.updateById(s);
                })
                .orElse(Boolean.FALSE);
    }

    public Boolean updateBatchMeetingSchedule(UserScheduleMeetingUpdateQuery updateQuery) {
        boolean updateResult = super.lambdaUpdate()
                .eq(UserSchedule::getMeetingId, updateQuery.getMeetingId())
                .set(UserSchedule::getDueDate, updateQuery.getDueDate())
                .set(UserSchedule::getRepeatFlag, updateQuery.getRepeatFlag())
                .update();
        // 增删关联的用户
        List<String> paramUserIds = updateQuery.getUserIds();
        if (!Objects.isNull(paramUserIds)) {
            Set<String> dbSet = super.lambdaQuery()
                    .eq(UserSchedule::getMeetingId, updateQuery.getMeetingId())
                    .list()
                    .stream()
                    .map(UserSchedule::getUserId)
                    .collect(Collectors.toSet());
            Set<String> paramSet = new HashSet<>(paramUserIds);
            Sets.SetView<String> deleteSet = Sets.difference(dbSet, paramSet);
            Sets.SetView<String> addSet = Sets.difference(paramSet, dbSet);
            if (!CollectionUtils.isEmpty(deleteSet)) {
                updateResult = updateResult && super.lambdaUpdate()
                        .eq(UserSchedule::getMeetingId, updateQuery.getMeetingId())
                        .in(UserSchedule::getUserId, deleteSet)
                        .remove();
            }
            if (!CollectionUtils.isEmpty(addSet)) {
                UserScheduleAddQuery scheduleAdd = new UserScheduleAddQuery()
                        .setUserId(updateQuery.getUserId())
                        .setTitle(updateQuery.getMeetingTitle())
                        .setContent(updateQuery.getMeetingContent())
                        .setMeetingId(updateQuery.getMeetingId())
                        .setAheadTime(updateQuery.getAheadTime())
                        .setDueDate(updateQuery.getDueDate())
                        .setStartTime(updateQuery.getStartTime())
                        .setEndTime(updateQuery.getEndTime())
                        .setRepeatFlag(updateQuery.getRepeatFlag());
                updateResult = updateResult && addBatchSchedule(scheduleAdd, addSet);
            }
        }
        return updateResult;
    }

    @Override
    public List<String> checkMeetingClash(UserMeetingClashQuery meetingClashQuery) {
        return this.baseMapper.checkMeetingClash(meetingClashQuery)
                .stream()
                .map(MeetingCheckInfo::realOrUserName)
                .collect(Collectors.toList());
    }
}
